﻿#include "pch.h"
#include "Client.h"
#include "Const.h"

CString Client::sUsername;  // 用户名

/**
 * 构造方法。
 * @param callback  回调
 */
Client::Client(CWnd* callback) :
    mCallback(callback),
    mFriendCount(0),
    mIconIndex(-1) {
    // 初始化网络
    WSADATA wsaData = { 0 };
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    // 连接服务器
    BOOL result = CSocket::Create(0, SOCK_STREAM, NULL);
    if (!result) {
        throw SOCKET_ERROR;
    }
    result = CSocket::Connect(ImProtocol::SERVER_IP, ImProtocol::SERVER_PORT);
    if (!result) {
        throw SOCKET_ERROR;
    }
}

/**
 * 析构方法。
 */
Client::~Client() {
    // 离线
    this->Logoff();

    // 延迟一下，防止数据还没发送完成就关闭网络
    Sleep(100);

    // 关闭网络
    CSocket::Close();
    WSACleanup();
}

/**
 * 添加好友。
 * @param friendName    好友名字
 */
ResultCode Client::AddFriend(LPCTSTR friendName) {
    // 检查是否超过好友上限
    if (mFriendCount > ImProtocol::MAX_USER_COUNT) {
        return TOO_MANY_FRIENDS;
    }

    // 检查好友名
    if (friendName == sUsername) {
        return FRIEND_NAME_EQUAL_SELF_NAME;
    }

    // 发送添加好友请求
    AddFriendRequest request(friendName, mIconIndex, sUsername);
    this->SendData(&request, sizeof(request));

    return SUCCEEDED;
}

/**
 * 允许或拒绝添加好友。
 * @param allow     允许则为 TRUE，否则为 FALSE
 * @param request   添加好友请求
 */
void Client::AllowAddFriend(BOOL allow, AddFriendRequest* request) {
    // 发送结果
    AddFriendResult addFriendResult(mIconIndex, request->mDstUsername, 
        allow ? ADD_FRIEND_ALLOW: ADD_FRIEND_NOT_ALLOW, request->mSrcUsername);
    this->SendData(&addFriendResult, sizeof(addFriendResult));

    // 如果允许，则好友数加1
    if (allow) {
        mFriendCount++;
    }
}

/**
 * 聊天。
 * @param chatInfo  聊天信息
 */
void Client::Chat(ChatInfo* chatInfo) {
    this->SendData(chatInfo, sizeof(ChatInfo));
}

/**
 * 获取好友数。
 * @return 好友数
 */
int Client::GetFriendCount() {
    return mFriendCount;
}

/**
 * 获取自己的名字。
 * @return 自己的名字
 */
LPCTSTR Client::GetUsername() {
    return sUsername;
}

/**
 * 处理添加好友请求。
 * @param request   添加好友请求
 */
void Client::HandleAddFriendRequest(AddFriendRequest* request) {
    mCallback->SendMessage(Const::UM_GET_ADD_FRIEND_REQUEST, (WPARAM)request);
}

/**
 * 处理添加好友结果。
 * @param result    添加好友结果
 */
void Client::HandleAddFriendResult(AddFriendResult* result) {
    if (result->mResultCode == ADD_FRIEND_ALLOW && result->mDstIconIndex != -1) {
        mFriendCount++;
    }

    mCallback->SendMessage(Const::UM_GET_ADD_FRIEND_RESULT, (WPARAM)result);
}

/**
 * 处理聊天。
 * @param chatInfo  聊天信息
 */
void Client::HandleChat(ChatInfo* chatInfo) {
    mCallback->SendMessage(Const::UM_CHAT_S2C, (WPARAM)chatInfo);
}

/**
 * 处理其他用户的离线请求。
 * @param request   其他用户的离线请求
 */
void Client::HandleFriendLogoff(LogoffRequest* request) {
    mCallback->SendMessage(Const::UM_FRIEND_LOGOFF, (WPARAM)request);
}

/**
 * 处理其他用户的登录消息。
 * @param friendLogon  其他用户的登录消息
 */
void Client::HandleFriendLogon(FriendLogon* friendLogon) {
    mCallback->SendMessage(Const::UM_FRIEND_LOGON, (WPARAM)friendLogon);
}

/**
 * 处理登录结果。
 * @param result    登录结果
 */
void Client::HandleLogonResult(LogonResult* result) {
    mCallback->SendMessage(Const::UM_GET_LOGON_RESULT, (WPARAM)result);

    if (result->mResultCode == LOGON_SUCCEEDED) {
        mIconIndex = result->mIconIndex;
        mFriendCount = result->mFriendCount;
    } else {
        sUsername.Empty();
    }
}

/**
 * 处理注册结果。
 * @param result    注册结果
 */
void Client::HandleRegResult(RegResult* result) {
    mCallback->SendMessage(Const::UM_GET_REG_RESULT, (WPARAM)result);

    if (result->mResultCode != REG_SUCCEEDED) {
        sUsername.Empty();
    }
}

/**
 * 离线。
 */
void Client::Logoff() {
    if (sUsername.IsEmpty()) {
        return;
    }

    LogoffRequest request(sUsername);
    this->SendData(&request, sizeof(request));

    sUsername.Empty();
}

/**
 * 登录。
 * @param username  用户名
 * @param password  密码
 */
void Client::Logon(LPCTSTR username, LPCTSTR password) {
    // 发送登录请求
    LogonRequest request(password, username);
    this->SendData(&request, sizeof(request));

    // 保存用户名
    sUsername = username;
}

/**
 * 处理服务器发送来的数据。
 * @param pro   协议
 */
void Client::OnGetMessageFromServer(ImProtocol* pro) {
    switch (pro->mType) {
        case ImProtocol::ADD_FRIEND_REQUEST:
            this->HandleAddFriendRequest((AddFriendRequest*)pro);
            break;
        case ImProtocol::ADD_FRIEND_RESULT:
            this->HandleAddFriendResult((AddFriendResult*)pro);
            break;
        case ImProtocol::CHAT:
            this->HandleChat((ChatInfo*)pro);
            break;
        case ImProtocol::FRIEND_LOGON:
            this->HandleFriendLogon((FriendLogon*)pro);
            break;
        case ImProtocol::LOGOFF:
            this->HandleFriendLogoff((LogoffRequest*)pro);
            break;
        case ImProtocol::LOGON:
            this->HandleLogonResult((LogonResult*)pro);
            break;
        case ImProtocol::REG:
            this->HandleRegResult((RegResult*)pro);
            break;
    }
}

/**
 * 接收数据。
 * @param errorCode 错误码
 */
void Client::OnReceive(int errorCode) {
    // 接收数据长度
    int size = 0;
    int read = CSocket::Receive((char*)&size, sizeof(size));
    if (read == SOCKET_ERROR) {
        CSocket::OnReceive(errorCode);
        return;
    }

    // 接收数据
    char* buffer = new char[size];
    read = CSocket::Receive(buffer, size);
    if (read <= 0) {
        delete[] buffer;
        CSocket::OnReceive(errorCode);
        return;
    }

    // 将数据转换成协议
    ImProtocol* pro = (ImProtocol*)buffer;

    // 处理协议
    this->OnGetMessageFromServer(pro);

    // 释放缓存
    delete[] buffer;

    AsyncSelect(FD_READ);
    CSocket::OnReceive(errorCode);
}

/**
 * 注册。
 * @param username  用户名
 * @param password  密码
 * @param iconIndex 头像索引
 */
void Client::Reg(LPCTSTR username, LPCTSTR password, int iconIndex) {
    // 发送注册请求
    RegRequest request(iconIndex, password, username);
    this->SendData(&request, sizeof(request));

    // 保存用户名和头像索引
    sUsername = username;
    mIconIndex = iconIndex;
}

/**
 * 发送数据。
 * @param data        数据
 * @param dataSize    数据长度
 * @return 实际发送的数据长度
 */
void Client::SendData(ImProtocol* data, int dataSize) {
    // 发送数据长度
    CSocket::Send(&dataSize, sizeof(dataSize), 0);

    // 发送数据
    CSocket::Send(data, dataSize, 0);

    // 防止发送过快导致服务器无法响应
    Sleep(100);
}
